import datetime
import json
import os
import queue
import sys
import threading
import tkinter as tk
from tkinter import ttk, messagebox
import requests
import configparser
import re

def resource_path(relative_path):
    """获取资源绝对路径"""
    try:
        # PyInstaller creates a temp folder and stores path in _MEIPASS
        base_path = sys._MEIPASS
        # print(f"[DEBUG] 打包资源路径: {base_path}")
    except Exception:
        base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        # print(f"[DEBUG] 开发资源路径: {base_path}")

    full_path = os.path.join(base_path, relative_path)
    # print(f"[DEBUG] 最终资源路径: {full_path}")
    return full_path


class ConfigManager:
    def __init__(self, file_path=resource_path("config/login.json")):
        self.file_path = file_path
        # print(f"[DEBUG] Config JSON path: {self.file_path}")  # 调试输出
        if not os.path.exists(self.file_path):
            print(f"Error: Config file not found at {self.file_path}")
            sys.exit(1)

        self.config = self._load_config()

    def _load_config(self):
        # print(f"[DEBUG] Loading config from: {self.file_path}")
        if os.path.exists(self.file_path):
            with open(self.file_path, "r", encoding="utf-8") as f:
                return json.load(f)
        else:
            print(f"Error: Config file not found at {self.file_path}")
            return {"domains": [], "recent_domains": []}

    def save_config(self):
        with open(self.file_path, "w", encoding="utf-8") as f:
            json.dump(self.config, f, indent=2)

    def add_domain(self, domain, username="", password="", save_credentials=False):
        # 更新最近使用的域名（最多保留10个）
        if domain in self.config["recent_domains"]:
            self.config["recent_domains"].remove(domain)
        self.config["recent_domains"].insert(0, domain)
        self.config["recent_domains"] = self.config["recent_domains"][:10]

        # 更新域名配置
        existing = next((d for d in self.config["domains"] if d["url"] == domain), None)
        if existing:
            if save_credentials:
                existing.update({"username": username, "password": password, "save_credentials": True})
        else:
            self.config["domains"].append({
                "url": domain,
                "username": username if save_credentials else "",
                "password": password if save_credentials else "",
                "save_credentials": save_credentials
            })
        self.save_config()


class LoginDialog(tk.Toplevel):
    def __init__(self, parent, config_manager):
        super().__init__(parent)
        self.parent = parent
        self.config_manager = config_manager
        self.token = None
        self.task_queue = queue.Queue()
        self.is_running = True  # 新增运行状态标志
        self.after_id = None  # 新增 after 任务ID
        self.title("用户登录")
        self.geometry("400x300")
        self.master = parent
        if not self.master.winfo_exists():
            raise RuntimeError("主窗口未正确初始化")

        # 初始化UI组件
        self._init_ui()

        # 启动队列处理循环
        # self.is_running = True
        self.after(100, self._process_queue)

    def _init_ui(self):  # 正确的方法定义，无额外参数
        """初始化UI组件"""
        # 域名输入框（下拉列表）
        self.label_domain = ttk.Label(self, text="域名:")
        self.label_domain.pack(pady=5)
        self.domain_var = tk.StringVar()
        self.combo_domain = ttk.Combobox(
            self,
            textvariable=self.domain_var,
            values=self.config_manager.config["recent_domains"]  # 使用 self.config_manager
        )
        self.combo_domain.pack(pady=5, fill=tk.X, padx=10)
        self.combo_domain.bind("<<ComboboxSelected>>", self._on_domain_selected)

        # 用户名输入
        self.label_username = ttk.Label(self, text="用户名:")
        self.label_username.pack(pady=5)
        self.entry_username = ttk.Entry(self)
        self.entry_username.pack(pady=5, fill=tk.X, padx=10)

        # 密码输入
        self.label_password = ttk.Label(self, text="密码:")
        self.label_password.pack(pady=5)
        self.entry_password = ttk.Entry(self, show="*")
        self.entry_password.pack(pady=5, fill=tk.X, padx=10)

        # 保存凭证勾选框
        self.save_var = tk.BooleanVar(value=False)
        self.check_save = ttk.Checkbutton(
            self,
            text="保存用户名密码",
            variable=self.save_var
        )
        self.check_save.pack(pady=5)

        # 登录按钮
        self.btn_login = ttk.Button(self, text="登录", command=self.start_login_thread)
        self.btn_login.pack(pady=10)

        # 错误提示
        self.label_error = ttk.Label(self, text="", foreground="red")
        self.label_error.pack()

    def _process_queue(self):
        """主线程处理队列中的UI任务"""
        if not self.is_running:
            return
        try:
            while True:
                task = self.task_queue.get_nowait()
                try:
                    if self.winfo_exists():  # 检查窗口是否仍存在
                        task()
                except tk.TclError:
                    pass  # 窗口已销毁，忽略剩余任务
        except queue.Empty:
            pass
        if self.is_running:
            self.after_id = self.after(100, self._process_queue)

    def destroy(self):
        """重写销毁方法，清理资源"""
        self.is_running = False
        if self.after_id:
            self.after_cancel(self.after_id)  # 取消未执行的 after 任务
        super().destroy()

    def _on_domain_selected(self, event):
        """选择域名后自动填充用户名密码"""
        domain = self.domain_var.get()
        domain_config = next(
            (d for d in self.config_manager.config["domains"] if d["url"] == domain),
            None
        )
        if domain_config and domain_config["save_credentials"]:
            self.entry_username.delete(0, tk.END)
            self.entry_username.insert(0, domain_config["username"])
            self.entry_password.delete(0, tk.END)
            self.entry_password.insert(0, domain_config["password"])
            self.save_var.set(True)
        else:
            self.save_var.set(False)

    def start_login_thread(self):
        """启动登录线程"""
        domain = self.domain_var.get().strip()
        username = self.entry_username.get().strip()
        password = self.entry_password.get().strip()
        save_credentials = self.save_var.get()

        if not all([domain, username, password]):
            self.label_error.config(text="域名、用户名和密码不能为空！")
            return

        # 检查域名是否以 http:// 或 https:// 开头
        if not domain.startswith(('http://', 'https://')):
            self.label_error.config(text="登陆地址需以 http:// 或 https:// 开头")
            return

        # 更新配置（主线程中执行）
        self.after(0, lambda: self.config_manager.add_domain(domain, username, password, save_credentials))

        # 禁用按钮并启动线程
        self.btn_login.config(state=tk.DISABLED)
        threading.Thread(
            target=self.login,
            args=(domain, username, password),
            daemon=True  # 设置为守护线程
        ).start()

    def login(self, domain, username, password):
        """子线程中执行登录逻辑"""
        error_msg = ""
        try:
            login_url = f"{domain}/plugin/adminapi/login.json"
            response = requests.post(
                login_url,
                data={"username": username, "password": password},
                timeout=10
            )
            response.raise_for_status()  # 触发HTTP错误
            data = response.json()
            if data.get("result") == 1:
                self.token = data["token"]
                self.task_queue.put(self.destroy)
                self.parent.after(0, self.parent.show_main_window)
            else:
                error_msg = f"登录失败: {data.get('info', '未知错误')}"
        except requests.exceptions.RequestException as e:
            error_msg = f"网络错误: {str(e)}"
        except json.JSONDecodeError as e:
            error_msg = f"响应解析错误: {str(e)}"
        except Exception as e:
            error_msg = f"未知错误: {str(e)}"
        finally:
            if error_msg:
                self.task_queue.put(lambda: self.label_error.config(text=error_msg))
            self.task_queue.put(self._safe_enable_login_button)

    def _safe_enable_login_button(self):
        # print("[DEBUG] 尝试恢复按钮状态")
        """安全恢复登录按钮状态"""
        """增加额外的存在性检查"""
        if not self.winfo_exists() or not self.btn_login.winfo_exists():
            return
        try:
            self.btn_login.config(state=tk.NORMAL)
        except tk.TclError as e:
            print(f"安全恢复按钮失败（窗口已关闭）: {str(e)}")


class NewsApp:
    def __init__(self):
        self.window = tk.Tk()
        self.window.title("文章生成与发布工具")
        self.window.geometry("1200x900")
        self.window.withdraw()
        self.config_manager = ConfigManager()

        self.titlepic_file = resource_path("config/titlepicimg.txt")
        self._init_titlepic_file()

        self.login_dialog = LoginDialog(self.window, self.config_manager)
        self.login_dialog.wait_window()
        # 检查登录结果
        if not getattr(self.login_dialog, 'token', None):
            self.window.quit()
            return

        # 存储Token和域名
        self.token = self.login_dialog.token
        self.base_domain = self.login_dialog.domain_var.get()

        self.generated_content = None
        self.generate_complete_event = threading.Event()  # 定义事件

        # 初始化API和角色
        self.api_var = tk.StringVar(value="讯飞星火")
        self.role_var = tk.StringVar(value="记者")
        self.selected_role_content = ""

        # 读取配置文件
        self.config = self._load_api_config()

        # 初始化主界面
        self._init_main_ui()
        self.show_main_window()

    def _init_selected_api(self):
        selected_api = self.api_var.get()
        if not selected_api:
            messagebox.showerror("错误", "请选择API！")
            return

        try:
            if not self.config.has_section(selected_api):
                raise ValueError(f"配置文件中未找到 {selected_api} 的配置节。")

            if not self.config.getboolean(selected_api, 'ENABLE'):
                raise ValueError(f"{selected_api} 接口未启用。")

            if not self.config.get(selected_api, 'APP_ID') or not self.config.get(selected_api, 'API_KEY') or not self.config.get(selected_api, 'API_SECRET') or not self.config.get(selected_api, 'API_URL'):
                raise ValueError(f"请在config.ini中编辑 {selected_api} 的配置。")

            api_config = self.config[selected_api]

            if selected_api == "讯飞星火":
                from spark_api import SparkAPI
                self.Ai_api = SparkAPI(api_config)
            elif selected_api == "百度文心":
                from wenxin_api import BaiduWenxinAPI
                self.Ai_api = BaiduWenxinAPI(api_config)
            elif selected_api == "阿里通义":
                from alitongyi_api import AliTongyiAPI
                self.Ai_api = AliTongyiAPI(api_config)
            elif selected_api == "Kimi":
                from kimi_api import KimiAPI
                self.Ai_api = KimiAPI(api_config)
            elif selected_api == "DeepSeek":
                from deepseek_api import DeepSeekAPI
                self.Ai_api = DeepSeekAPI(api_config)
            else:
                raise ValueError("未知的API名称")
        except ValueError as e:
            messagebox.showerror("错误", str(e))
            # 恢复下拉框的初始状态并显示提示词
            self.api_var.set("")  # 清空当前选择
            self._on_api_postcommand()  # 显示提示词

    def _load_api_config(self):
        config = configparser.ConfigParser()
        config_file_path = resource_path('config/config.ini')
        # print(f"Config file path: {config_file_path}")  # 打印配置文件路径
        config.read(config_file_path, encoding='utf-8')
        # print(f"Loaded config sections: {config.sections()}")  # 打印配置节
        return config
    def _init_titlepic_file(self):
        """确保图片配置文件存在"""
        try:
            if not os.path.exists(self.titlepic_file):
                with open(self.titlepic_file, 'w') as f:
                    f.write("# 默认图片URL\n")
        except Exception as e:
            print(f"初始化图片配置文件失败: {str(e)}")

    def _get_titlepics(self):
        """获取所有标题图片"""
        try:
            with open(self.titlepic_file, 'r', encoding='utf-8') as f:
                return [line.strip() for line in f
                        if line.strip() and not line.startswith("#")]
        except Exception as e:
            print(f"读取标题图片失败: {str(e)}")
            return []

    def show_main_window(self):
        """完全重构的主窗口显示方法"""
        if not self.window.winfo_exists():
            return
        try:
            self.window.deiconify()
            # 确保主循环只启动一次
            if not hasattr(self, "_mainloop_started"):
                self._mainloop_started = True
                self.window.mainloop()
        except tk.TclError as e:
            print(f"主窗口显示异常: {str(e)}")

    def _init_main_ui(self):
        # 初始化API客户端
        self._init_selected_api()

        # 主框架容器
        self.main_frame = ttk.Frame(self.window)
        self.main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # 创建 PanedWindow
        self.paned_window = ttk.PanedWindow(self.main_frame, orient=tk.HORIZONTAL)
        self.paned_window.pack(fill=tk.BOTH, expand=True)

        # 左侧部分
        self.left_frame = ttk.Frame(self.paned_window)
        self.paned_window.add(self.left_frame, weight=1)

        # API设定区域
        self.frame_api_settings = ttk.LabelFrame(self.left_frame, text="API设定")
        self.frame_api_settings.pack(fill=tk.X, pady=5)

        # 创建一个 Frame 来容纳 API 下拉框、角色下拉框和添加角色按钮
        self.api_role_frame = ttk.Frame(self.frame_api_settings)
        self.api_role_frame.pack(fill=tk.X, pady=5)

        # API下拉选择框
        self.api_var = tk.StringVar()
        self.combo_api = ttk.Combobox(
            self.api_role_frame,
            textvariable=self.api_var,
            values=self.config.sections(),  # 使用配置文件中的所有节名称
            state="readonly"  # 设置为只读
        )
        self.combo_api.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.X, expand=True)
        self.combo_api.bind("<<ComboboxSelected>>", self._on_api_selected)
        self.combo_api.bind("<<Combobox>>", self._on_api_postcommand)  # 绑定 postcommand 事件
        # 设置默认值为第一个选项
        if self.config.sections():
            self.api_var.set(self.config.sections()[0])  # 默认选第一个配置项
        self._on_api_postcommand()  # 初始化时设置提示词

        # 角色选择框
        self.role_var = tk.StringVar()
        self.combo_role = ttk.Combobox(
            self.api_role_frame,
            textvariable=self.role_var,
            values=self._load_roles(),
            state="readonly"  # 设置为只读
        )
        self.combo_role.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.X, expand=True)
        self.combo_role.bind("<<ComboboxSelected>>", self._on_role_selected)
        self.combo_role.bind("<<Combobox>>", self._on_role_postcommand)  # 绑定 postcommand 事件
        # 设置默认值为第一个角色
        # if roles := self._load_roles():
        #     self.role_var.set(roles[0])  # 默认选第一个角色
        # self._on_role_postcommand()  # 初始化时设置提示词

        # 添加角色按钮
        self.btn_add_role = ttk.Button(
            self.api_role_frame,
            text="添加角色",
            command=self._show_add_role_dialog
        )
        self.btn_add_role.pack(side=tk.LEFT, padx=5, pady=5)

        # 角色编辑框
        self.text_role_content = tk.Text(self.frame_api_settings, height=5, font=("宋体", 12), state='disabled')  # 设置为只读
        self.text_role_content.pack(pady=5, fill=tk.BOTH, padx=10)

        # 输入主题区域
        self.frame_topic = ttk.LabelFrame(self.left_frame, text="主题")
        self.frame_topic.pack(fill=tk.X, pady=5)
        self.entry_topic = ttk.Entry(self.frame_topic, width=80)
        self.entry_topic.pack(padx=5, pady=5)

        # 生成按钮
        self.btn_generate = ttk.Button(
            self.left_frame,
            text="生成文章",
            command=self.start_generate_thread
        )
        self.btn_generate.pack(pady=5)

        # 操作日志显示区域
        self.frame_log = ttk.LabelFrame(self.left_frame, text="操作日志")
        self.frame_log.pack(fill=tk.BOTH, expand=True, pady=5)

        # 创建 Text 小部件和 Scrollbar 小部件
        self.log_text = tk.Text(self.frame_log, height=5, font=("宋体", 12))
        self.log_scrollbar = ttk.Scrollbar(self.frame_log, orient=tk.VERTICAL, command=self.log_text.yview)
        self.log_text.configure(yscrollcommand=self.log_scrollbar.set)

        # 使用 grid 布局来放置 Text 和 Scrollbar
        self.log_text.grid(row=0, column=0, sticky="nsew")
        self.log_scrollbar.grid(row=0, column=1, sticky="ns")

        # 配置 grid 布局的权重，使 Text 小部件可以扩展
        self.frame_log.grid_rowconfigure(0, weight=1)
        self.frame_log.grid_columnconfigure(0, weight=1)

        # 发布选项区域
        self.frame_publish_options = ttk.LabelFrame(self.left_frame, text="发布选项")
        self.frame_publish_options.pack(fill=tk.X, pady=5)
        self.frame_publish_options.columnconfigure(1, weight=1)

        # 文章标题
        ttk.Label(self.frame_publish_options, text="文章标题:").grid(row=0, column=0, padx=5)
        self.entry_title = ttk.Entry(self.frame_publish_options, width=80)
        self.entry_title.grid(row=0, column=1, columnspan=3, padx=5, sticky="ew")

        # 分类ID
        ttk.Label(self.frame_publish_options, text="分类ID:").grid(row=1, column=0, padx=5)
        self.entry_category = ttk.Entry(self.frame_publish_options, width=10)
        self.entry_category.grid(row=1, column=1, columnspan=3, padx=5, sticky=tk.W)

        # 栏目ID (cid)
        ttk.Label(self.frame_publish_options, text="栏目ID (cid):").grid(row=2, column=0, padx=5)
        self.entry_cid = ttk.Entry(self.frame_publish_options, width=10)
        self.entry_cid.grid(row=2, column=1, columnspan=3, padx=5, sticky=tk.W)

        # 文章简介 (intro)
        ttk.Label(self.frame_publish_options, text="文章简介:").grid(row=3, column=0, padx=5)
        self.entry_intro = ttk.Entry(self.frame_publish_options, width=80)
        self.entry_intro.grid(row=3, column=1, columnspan=3, padx=5, sticky="ew")

        # 上传图片组件
        ttk.Label(self.frame_publish_options, text="图片上传：").grid(row=4, column=0, padx=5)
        self.entry_image_path = ttk.Entry(self.frame_publish_options, width=50)
        self.entry_image_path.grid(row=4, column=1, padx=5, sticky="ew")

        ttk.Button(
            self.frame_publish_options,
            text="浏览",
            command=self.browse_image
        ).grid(row=4, column=2, padx=5)

        self.btn_upload = ttk.Button(
            self.frame_publish_options,
            text="上传",
            command=self.start_upload_thread
        )
        self.btn_upload.grid(row=4, column=3, padx=5)

        # 标题图片URL (titlepic)
        ttk.Label(self.frame_publish_options, text="标题图片URL:").grid(row=5, column=0, padx=5)
        self.entry_titlepic = ttk.Entry(self.frame_publish_options, width=60)
        self.entry_titlepic.grid(row=5, column=1, columnspan=2, padx=5, sticky="ew")

        # 随机图片按钮
        ttk.Button(
            self.frame_publish_options,
            text="随机图片",
            command=self.random_titlepic
        ).grid(row=5, column=3, padx=5)

        # 图片预览区
        ttk.Label(self.frame_publish_options, text="图片预览:").grid(row=6, column=0, padx=5, sticky=tk.N)
        self.preview_label = ttk.Label(self.frame_publish_options)
        self.preview_label.grid(row=6, column=1, columnspan=2, padx=5, pady=5, sticky="ew")

        # 预览图片按钮
        self.btn_toggle_preview = ttk.Button(
            self.frame_publish_options,
            text="预览图片",  # 初始状态
            command=self.toggle_preview,
            width=11
        )
        self.btn_toggle_preview.grid(row=6, column=3, padx=5, sticky=tk.E)

        # 日期 (extenddate)
        ttk.Label(self.frame_publish_options, text="日期").grid(row=7, column=0, padx=5)
        self.entry_extenddate = ttk.Entry(self.frame_publish_options, width=10)
        self.entry_extenddate.grid(row=7, column=1, columnspan=3, padx=5, sticky=tk.W)
        self.entry_extenddate.insert(0, datetime.datetime.now().strftime("%Y-%m-%d"))  # 新增

        # 发布按钮
        self.btn_publish = ttk.Button(
            self.left_frame,
            text="发布文章",
            command=self.start_publish_thread,
        )
        self.btn_publish.pack(pady=10)

        # 自动运行区域
        self.frame_auto_run = ttk.LabelFrame(self.left_frame, text="自动运行")
        self.frame_auto_run.pack(fill=tk.X, pady=5)

        ttk.Label(self.frame_auto_run, text="配置文件:").grid(row=0, column=0, padx=5)
        self.entry_config_file = ttk.Entry(self.frame_auto_run, width=50)
        self.entry_config_file.grid(row=0, column=1, padx=5, sticky="ew")

        ttk.Button(
            self.frame_auto_run,
            text="浏览",
            command=self.browse_config_file
        ).grid(row=0, column=2, padx=5)

        self.btn_auto_run = ttk.Button(
            self.frame_auto_run,
            text="自动运行",
            command=self.start_auto_run_thread
        )
        self.btn_auto_run.grid(row=1, column=0, columnspan=3, pady=5)

        # 右侧部分
        self.right_frame = ttk.Frame(self.paned_window)
        self.paned_window.add(self.right_frame, weight=2)

        # 内容编辑区
        self.frame_content = ttk.LabelFrame(self.right_frame, text="文章内容")
        self.frame_content.pack(fill=tk.BOTH, expand=True)
        self.text_content = tk.Text(self.frame_content, height=20, font=("宋体", 12))
        self.text_content.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # 插入图片按钮
        self.btn_insert_image = ttk.Button(
            self.frame_content,
            text="插入图片",
            command=self.insert_image
        )
        self.btn_insert_image.pack(side=tk.TOP, pady=5)

        # 更新全站区域
        self.frame_update_site = ttk.LabelFrame(self.right_frame, text="更新全站")
        self.frame_update_site.pack(fill=tk.X, pady=5)

        # 更新全站按钮
        self.btn_update_site = ttk.Button(
            self.frame_update_site,
            text="更新全站",
            command=self.update_site
        )
        self.btn_update_site.pack(side=tk.LEFT, padx=5, pady=5)

        # 自动运行后更新全站复选框
        self.auto_update_var = tk.BooleanVar(value=False)
        self.check_auto_update = ttk.Checkbutton(
            self.frame_update_site,
            text="自动运行后更新全站",
            variable=self.auto_update_var
        )
        self.check_auto_update.pack(side=tk.LEFT, padx=5, pady=5)

        #处理内容按钮
        self.btn_process_content = ttk.Button(
            self.frame_content,
            text="处理内容",
            command=self.process_generated_content
        )
        self.btn_process_content.pack(side=tk.TOP, pady=5)

    def _on_api_postcommand(self, event=None):
        if not self.api_var.get():
            self.combo_api.set("请选择API接口")

    def _on_role_postcommand(self, event=None):
        if not self.role_var.get():
            self.combo_role.set("请选择角色")

    def _load_roles(self):
        """从 systemrole.ini 文件中加载角色列表"""
        role_config = configparser.ConfigParser()
        role_config_file_path = resource_path('config/systemrole.ini')
        print(f"Role config file path: {role_config_file_path}")  # 打印配置文件路径
        role_config.read(role_config_file_path, encoding='utf-8')
        print(f"Loaded role sections: {role_config.sections()}")  # 打印配置节

        roles = []
        for section in role_config.sections():
            role_name = role_config.get(section, 'Role_Name')
            roles.append(role_name)

        return roles

    def _on_role_selected(self, event):
        """处理角色选择框的事件"""
        selected_role = self.role_var.get()
        if not selected_role:
            messagebox.showinfo("提示", "请选择一个角色！")
            return

        # 获取角色内容并显示在文本框中
        role_config = configparser.ConfigParser()
        role_config_file_path = resource_path('config/systemrole.ini')
        role_config.read(role_config_file_path, encoding='utf-8')

        for section in role_config.sections():
            if role_config.get(section, 'Role_Name') == selected_role:
                role_content = role_config.get(section, 'Role_Content')
                self.text_role_content.config(state='normal')
                self.text_role_content.delete(1.0, tk.END)
                self.text_role_content.insert(tk.END, role_content)
                self.selected_role_content = role_content
                self.text_role_content.config(state='disabled')
                break

    def _on_api_selected(self, event):
        self._init_selected_api()

    def _show_add_role_dialog(self):
        def on_save(role_name, role_content):
            self._add_role_to_ini(role_name, role_content)
            self._update_role_combobox()

        dialog = AddRoleDialog(self.window, on_save)
        dialog.wait_window()

    def _add_role_to_ini(self, role_name, role_content):
        role_config = configparser.ConfigParser()
        role_config_file_path = resource_path('config/systemrole.ini')
        role_config.read(role_config_file_path, encoding='utf-8')

        # 找到下一个可用的 section 名称
        new_section = f"role{len(role_config.sections()) + 1}"
        role_config[new_section] = {
            'Role_Name': role_name,
            'Role_Content': role_content
        }

        with open(role_config_file_path, 'w', encoding='utf-8') as f:
            role_config.write(f)

    def _update_role_combobox(self):
        roles = self._load_roles()
        self.combo_role['values'] = roles
        if roles:
            self.combo_role.set(roles[0])  # 设置默认选择

    def browse_config_file(self):
        """打开文件选择对话框"""
        from tkinter import filedialog
        file_path = filedialog.askopenfilename(
            title="选择配置文件",
            filetypes=[("Excel files", "*.xls *.xlsx")]
        )
        if file_path:
            self.entry_config_file.delete(0, tk.END)
            self.entry_config_file.insert(0, file_path)

    def start_auto_run_thread(self):
        """启动自动运行线程"""
        config_file = self.entry_config_file.get().strip()
        if not config_file:
            messagebox.showerror("错误", "请先选择配置文件！")
            return

        self.btn_auto_run.config(state=tk.DISABLED)
        threading.Thread(target=self.auto_run, args=(config_file,)).start()

    def auto_run(self, config_file):
        """自动运行逻辑"""
        import pandas as pd

        try:
            # 读取Excel文件
            df = pd.read_excel(config_file, na_filter=False)

            # 确保所有需要处理的列都是字符串类型
            df['CID'] = df['CID'].astype(str)
            df['topic'] = df['topic'].astype(str)
            df['text'] = df['text'].astype(str)
            df['title'] = df['title'].astype(str)
            df['titlepic'] = df['titlepic'].astype(str)
            df['extenddate'] = df['extenddate'].astype(str)
            df['public'] = df['public'].astype(str)

            df = df.replace('nan', '')

            for index, row in df.iterrows():
                cid = row['CID']
                topic = row['topic']
                text = row['text']
                title = row['title']
                titlepic = row['titlepic']
                extenddate = row['extenddate']
                public = row['public']

                self.window.update()  # 新增强制刷新

                # self.log_message(f"处理行: {index}\n")
                # self.log_message(f"CID: {cid}, Topic: {topic}, Text: {text[:50]}...\n")
                # self.log_message(f"Titlepic: {titlepic}, Extenddate: {extenddate}\n")

                if public == '1':
                    continue

                if len(text) < 10:
                    self.log_message(f"生成内容，主题: {topic}\n")
                    self.entry_topic.delete(0, tk.END)
                    self.entry_topic.insert(0, topic)
                    self.entry_title.delete(0, tk.END)
                    self.start_generate_thread()
                    # 使用事件机制等待生成内容完成
                    self.generate_complete_event.wait()
                    self.generate_complete_event.clear()  # 重置事件
                    # self.process_generated_content()
                    text = self.text_content.get(1.0, tk.END).strip()
                    if len(self.entry_title.get()) > 5:
                        title = self.entry_title.get()
                    # title = self.entry_title.get()  # 获取生成的标题
                    intro = self.entry_intro.get()
                else:
                    if len(title) < 5:
                        title = topic  # 使用topic作为标题
                    intro = ""

                if len(title) < 5:
                    title = topic

                # 确保 titlepic 正确设置
                if pd.isna(titlepic) or not titlepic.strip():
                    self.random_titlepic()
                    titlepic = self.entry_titlepic.get()
                    self.window.update()  # 新增强制刷新

                # 确保 extenddate 正确设置
                if pd.isna(extenddate) or not extenddate.strip():
                    extenddate = datetime.datetime.now().strftime("%Y-%m-%d")
                    self.entry_extenddate.delete(0, tk.END)
                    self.entry_extenddate.insert(0, extenddate)
                    self.window.update()  # 新增强制刷新

                self.window.update()  # 新增强制刷新

                self.log_message(f"发布文章，CID: {cid}, 标题: {title}，图片: {titlepic}，扩展日期: {extenddate}\n")
                response = self.publish_article(title, text, cid, self.token, intro, titlepic, extenddate)

                if response["result"] == 1:
                    # 更新Excel文件
                    df.at[index, 'text'] = text
                    df.at[index, 'title'] = title  # 更新title列
                    df.at[index, 'public'] = '1'  # 确保public列是字符串类型
                    df.at[index, 'titlepic'] = titlepic
                    df.at[index, 'extenddate'] = extenddate
                    df.to_excel(config_file, index=False)

                self.entry_topic.delete(0, tk.END)

            # 保存更新后的Excel文件
            df.to_excel(config_file, index=False)
            # 如果勾选了自动运行后更新全站，则执行更新全站操作
            if self.auto_update_var.get():
                self.update_site()
            self.log_message("自动运行完成！\n")
        except Exception as e:
            self.log_message(f"自动运行出错：{str(e)}\n")
        finally:
            self.window.after(0, lambda: self.btn_auto_run.config(state=tk.NORMAL))

    def process_generated_content(self):
        """处理生成的内容"""
        # 分割生成的内容为段落
        # paragraphs = self.generated_content.split('\n')
        paragraphs = self.text_content.get(1.0, tk.END).strip().split('\n')

        symbol_pattern = r'[#*]+'
        for i, paragraph in enumerate(paragraphs):
            # 移除段落中的符号
            paragraphs[i] = re.sub(symbol_pattern, '', paragraph)
        # 检查第一段是否包含新闻标题
        if paragraphs:
            first_paragraph = paragraphs[0].strip()
            if first_paragraph:
                # 假设新闻标题的格式是 <p>标题：...</p>
                match = re.match(r'<p>标题：(.*?)</p>', first_paragraph)
                if match:
                    title = match.group(1)
                    self.entry_title.delete(0, tk.END)
                    self.entry_title.insert(0, title)
                    # 删除第一段
                    paragraphs = paragraphs[1:]

            # 检查每个段落是否包含“X网”和“年月日”，并删除这些段落
            filtered_paragraphs = []
            for paragraph in paragraphs:
                if not re.search(r'\bX+报\b|\bX+网\b|\bX+年\b|\bX+月\b|\bX+日\b', paragraph):
                    filtered_paragraphs.append(paragraph)

        # 检查结尾段落是否包含“XXX报道”、“XXX编辑”等，并删除这些段落
        final_paragraphs = []
        for paragraph in filtered_paragraphs:
            if not re.search(r'(报道|编辑|撰写)', paragraph):
                final_paragraphs.append(paragraph)

        # 重新组合处理后的内容
        processed_content = '\n'.join(final_paragraphs)

        # 更新内容编辑区
        self.text_content.delete(1.0, tk.END)
        self.text_content.insert(tk.END, processed_content)

    def insert_image(self):
        """打开文件选择对话框并上传图片"""
        from tkinter import filedialog
        file_path = filedialog.askopenfilename(
            title="选择图片文件",
            filetypes=[("图片文件", "*.jpg *.jpeg *.png")]
        )
        if file_path:
            self.start_upload_image_thread(file_path)

    def log_message(self, message):
        self.log_text.insert(tk.END, message + "\n")
        self.log_text.see(tk.END)
    def toggle_preview(self):
        """切换预览状态"""
        if hasattr(self, 'preview_image') and self.preview_image:
            self.clear_preview()
        else:
            self.start_preview_thread()

    def random_titlepic(self):
        """随机选择标题图片"""
        titlepics = self._get_titlepics()
        if not titlepics:
            messagebox.showinfo("提示", "暂无可用图片，请先上传")
            return

        import random
        selected = random.choice(titlepics)
        self.entry_titlepic.delete(0, tk.END)
        self.entry_titlepic.insert(0, selected)
        self.start_preview_thread()

    def start_generate_thread(self):
        """启动生成线程"""
        topic = self.entry_topic.get()
        if not topic:
            messagebox.showerror("错误", "请输入主题！")
            return

        self._init_selected_api()

        self.btn_generate.config(state=tk.DISABLED)
        self.log_message("开始生成文章...\n")
        self.window.after(0, lambda: threading.Thread(target=self.generate_news, args=(topic,)).start())

    def generate_news(self, topic):
        """生成文章（不自动发布）"""
        try:
            self.btn_generate.config(state=tk.DISABLED)
            self.log_message("正在生成文章，请稍候...\n")
            # 清空旧内容
            self.text_content.delete(1.0, tk.END)
            self.generated_content = self.Ai_api.generate_news(topic, self.selected_role_content)
            if self.generated_content:
                # 使用主线程安全的方式更新UI
                self.window.after(0, self._update_content_ui)
                self.log_message("文章生成成功！\n")
                self.btn_publish.config(state=tk.NORMAL)
                # 处理生成的内容
                self.window.after(0, self.process_generated_content)
                self.window.after(0, self.process_generated_content)
                self.generate_complete_event.set()  # 通知主线程生成完成
            else:
                self.log_message("文章生成失败！\n")
        except Exception as e:
            messagebox.showerror("错误", str(e))
        finally:
            self.btn_generate.config(state=tk.NORMAL)

    def _update_content_ui(self):
        """在主线程中更新内容显示"""
        self.text_content.delete(1.0, tk.END)
        self.text_content.insert(tk.END, self.generated_content)
        self.window.after(0, lambda: self.window.setvar('generated_content', self.generated_content))  # 设置变量

    def browse_image(self):
        """打开文件选择对话框"""
        from tkinter import filedialog
        file_path = filedialog.askopenfilename(
            title="选择图片文件",
            filetypes=[("图片文件", "*.jpg *.jpeg *.png")]
        )
        if file_path:
            self.entry_image_path.delete(0, tk.END)
            self.entry_image_path.insert(0, file_path)

    def start_upload_thread(self):
        """启动图片上传线程"""
        file_path = self.entry_image_path.get().strip()
        if not file_path:
            messagebox.showerror("错误", "请先选择图片文件！")
            return
        if not os.path.isfile(file_path):
            messagebox.showerror("错误", "文件不存在，请重新选择！")
            return

        self.btn_upload.config(state=tk.DISABLED)
        self.log_message("开始上传图片...\n")
        threading.Thread(target=self.upload_image, args=(file_path,)).start()

    def start_upload_image_thread(self, file_path):
        """启动图片上传线程"""
        if not file_path:
            messagebox.showerror("错误", "请先选择图片文件！")
            return
        if not os.path.isfile(file_path):
            messagebox.showerror("错误", "文件不存在，请重新选择！")
            return

        self.btn_insert_image.config(state=tk.DISABLED)
        self.log_message("开始上传图片...\n")
        threading.Thread(target=self.upload_image_insert, args=(file_path,)).start()

    def upload_image(self, file_path):
        """执行图片上传"""
        try:
            api_url = f"{self.base_domain}/plugin/adminapi/site/uploadImage.json"
            with open(file_path, 'rb') as f:
                response = requests.post(
                    api_url,
                    files={'image': f},
                    data={'token': self.token}
                )
            data = response.json()

            if data.get('result') == 1:
                from urllib.parse import urljoin
                full_url = urljoin(self.base_domain, data.get('url'))

                try:
                    with open(self.titlepic_file, 'a', encoding='utf-8') as f:
                        f.write(f"\n{full_url}")
                except Exception as e:
                    print(f"保存图片URL失败: {str(e)}")

                self.window.after(0, lambda: self.entry_titlepic.delete(0, tk.END))
                self.window.after(0, lambda: self.entry_titlepic.insert(0, full_url))
                self.log_message("图片上传成功！URL已自动填充\n")
                self.start_preview_thread()
            else:
                error_msg = data.get('info', '未知错误')
                self.log_message(f"上传失败：{error_msg}\n")
        except Exception as e:
            self.log_message(f"上传出错：{str(e)}\n")
        finally:
            self.window.after(0, lambda: self.btn_upload.config(state=tk.NORMAL))

    def upload_image_insert(self, file_path):
        """执行图片上传"""
        try:
            api_url = f"{self.base_domain}/plugin/adminapi/site/uploadImage.json"
            with open(file_path, 'rb') as f:
                response = requests.post(
                    api_url,
                    files={'image': f},
                    data={'token': self.token}
                )
            data = response.json()

            if data.get('result') == 1:
                from urllib.parse import urljoin
                full_url = urljoin(self.base_domain, data.get('url'))

                try:
                    with open(self.titlepic_file, 'a', encoding='utf-8') as f:
                        f.write(f"\n{full_url}")
                except Exception as e:
                    print(f"保存图片URL失败: {str(e)}")

                # 获取文件名
                file_name = os.path.basename(file_path)

                # 将图片链接插入到内容编辑区鼠标所在位置
                self.insert_image_link(full_url, file_name)

                self.log_message("图片上传成功！\n")
            else:
                error_msg = data.get('info', '未知错误')
                self.log_message(f"上传失败：{error_msg}\n")
        except Exception as e:
            self.log_message(f"上传出错：{str(e)}\n")
        finally:
            self.window.after(0, lambda: self.btn_insert_image.config(state=tk.NORMAL))

    def insert_image_link(self, url, file_name):
        """将图片链接插入到内容编辑区鼠标所在位置"""
        # 获取鼠标插入位置
        insert_pos = self.text_content.index(tk.INSERT)

        # 创建图片链接
        img_tag = f'<img src="{url}" title="{file_name}" alt="{file_name}"/>'

        # 插入图片链接
        self.text_content.insert(insert_pos, img_tag)

    # 新增图片预览方法
    def start_preview_thread(self):
        """启动预览线程"""
        url = self.entry_titlepic.get().strip()
        if not url:
            messagebox.showinfo("提示", "请输入图片URL")
            return
        # 先显示加载状态
        self.window.after(0, lambda: self.preview_label.config(text="加载中..."))
        threading.Thread(target=self.load_and_show_image, args=(url,)).start()

    def load_and_show_image(self, url):
        """加载并显示图片"""
        try:
            response = requests.get(url, timeout=10)
            if response.status_code == 200:
                # 使用PIL处理图片
                from PIL import Image, ImageTk
                import io

                img_data = response.content
                img = Image.open(io.BytesIO(img_data))

                # 调整图片尺寸（限制最大尺寸为300x200）
                max_size = (200, 150)
                img.thumbnail(max_size)

                photo_img = ImageTk.PhotoImage(img)

                # 在主线程更新图片
                self.window.after(0, lambda: self.update_preview(photo_img))
        except Exception as e:
            self.log_message(f"图片加载失败: {str(e)}\n")
            # 加载失败时恢复状态
            self.window.after(0, lambda: self.preview_label.config(text=""))
            self.window.after(0, self._update_button_state)

    def update_preview(self, photo_img):
        """安全更新预览图片"""
        self.preview_image = photo_img  # 保持引用
        self.preview_label.configure(image=photo_img)
        self.window.after(0, self._update_button_state)

    def clear_preview(self):
        """清除图片预览"""
        self.preview_label.configure(image='')
        self.preview_image = None  # 释放图片引用
        self.window.after(0, self._update_button_state)

    def _update_button_state(self):
        """更新按钮显示状态"""
        if hasattr(self, 'preview_image') and self.preview_image:
            self.btn_toggle_preview.config(text="取消预览")
        else:
            self.btn_toggle_preview.config(text="预览图片")
    def start_publish_thread(self):
        # 获取参数
        title = self.entry_title.get()
        text = self.text_content.get(1.0, tk.END).strip()
        cid = self.entry_cid.get()
        token = self.token
        intro = self.entry_intro.get()
        titlepic = self.entry_titlepic.get()
        extenddate = self.entry_extenddate.get()

        # 必填参数校验
        if not all([title, text, cid, token]):
            messagebox.showerror("错误", "标题、内容、栏目ID和Token均不能为空！")
            return
        try:
            cid = int(cid)  # 强制转换为整数
        except ValueError:
            messagebox.showerror("错误", "栏目ID必须是数字！")
            return

        # 调用发布方法
        threading.Thread(
            target=self.publish_article,
            args=(title, text, cid, token, intro, titlepic, extenddate)
        ).start()

    def publish_article(self, title, text, cid, token, intro, titlepic, extenddate):
        """发布文章"""
        api_url = f"{self.base_domain}/plugin/adminapi/site/news/save.json"
        try:
            response = requests.post(
                api_url,
                data={
                    "title": title,
                    "text": text,
                    "cid": cid,
                    "intro": intro,
                    "titlepic": titlepic,
                    "token": token,
                    "extend.date": extenddate,
                },
                timeout=90
            )
            response.raise_for_status()
            data = response.json()
            if data["result"] == 1:
                self.log_message("文章发布成功！文章id=" + data["info"] + "\n")
            elif data["result"] == 0:
                self.log_message("文章发布失败！错误信息：" + data["info"] + "\n")
            elif data["result"] == 2:
                self.log_message("登录失效，请重新登陆\n")
            else:
                self.log_message("文章发布失败！未知错误！\n")
        except Exception as e:
            # 确保返回标准结构
            return {"result": -1, "info": str(e), "response": response.text[:200] if response else ""}

        # 确保data不为None
        return data if data else {"result": -1, "info": "Empty response"}
        # 调试输出
        """
        # print("[DEBUG] API URL:", api_url)
        # print("[DEBUG] 响应状态码:", response.status_code)
        # print("[DEBUG] 响应内容:", response.text)
        """

    def update_site(self):
        """通过API更新全站"""
        try:
            update_url = f"{self.base_domain}/plugin/adminapi/site/generate.json"
            response = requests.post(
                update_url,
                data={"token": self.token},
                timeout=600
            )
            response.raise_for_status()
            data = response.json()
            if data.get("result") == 1:
                self.log_message("全站更新成功！\n")
            else:
                error_msg = f"全站更新失败: {data.get('info', '未知错误')}"
                self.log_message(f"{error_msg}\n")
        except Exception as e:
            self.log_message(f"更新全站出错：{str(e)}\n")


class AddRoleDialog(tk.Toplevel):
    def __init__(self, parent, on_save):
        super().__init__(parent)
        self.title("添加角色")
        self.geometry("400x400")
        self.on_save = on_save

        # 角色名称输入框
        self.label_role_name = ttk.Label(self, text="角色名称:")
        self.label_role_name.pack(pady=5)
        self.entry_role_name = ttk.Entry(self, width=40)
        self.entry_role_name.pack(pady=5, fill=tk.X, padx=10)

        # 角色描述输入框
        self.label_role_content = ttk.Label(self, text="角色描述:")
        self.label_role_content.pack(pady=5)
        self.text_role_content = tk.Text(self, height=10, font=("宋体", 12))
        self.text_role_content.pack(pady=5, fill=tk.BOTH, padx=10)

        # 保存按钮
        self.btn_save = ttk.Button(self, text="保存", command=self._save_role)
        self.btn_save.pack(pady=10)

    def _save_role(self):
        role_name = self.entry_role_name.get().strip()
        role_content = self.text_role_content.get(1.0, tk.END).strip()

        if not role_name or not role_content:
            messagebox.showerror("错误", "角色名称和角色描述不能为空！")
            return

        self.on_save(role_name, role_content)
        self.destroy()

if __name__ == "__main__":
    import traceback

    try:
        app = NewsApp()
    except Exception as e:
        with open("error.log", "w") as f:
            f.write(f"崩溃原因: {str(e)}\n")
            f.write(traceback.format_exc())
        messagebox.showerror("程序崩溃", f"错误已记录到 error.log\n{str(e)}")
